package com.skynet.supervision.aop.uac;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

import com.skynet.supervision.aop.uac.beans.UACUserInfo;
import com.skynet.supervision.common.ErrCode;
import com.skynet.supervision.common.Result;
import com.skynet.supervision.dao.UserAccountMapper;
import com.skynet.supervision.dao.UserInfosMapper;
import com.skynet.supervision.utils.*;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Optional;

/**
 * @author xuchao
 * @date 2023-03-08 20:23
 */
@SuppressWarnings("unused")
@Slf4j
public class AuthorizationInterceptor implements HandlerInterceptor {

    public static final String USER_KEY = "skynet-uac-user";

    @Resource
    private UACClientProperties properties;
    private final JwtUtil jwtUtil;

    private final UserAccountMapper userAccountMapper;

    private final UserInfosMapper userInfosMapper;

    public AuthorizationInterceptor(JwtUtil jwtUtil, UserAccountMapper userAccountMapper, UserInfosMapper userInfosMapper) {
        this.jwtUtil = jwtUtil;
        this.userAccountMapper = userAccountMapper;
        this.userInfosMapper = userInfosMapper;
    }

    private Result wrapException(Boolean enableAuth, Exception ex) {
        if (!enableAuth) {
            return null;
        }
        return Errors.wrapException(ErrCode.NORMAL_ERROR, ex);
    }

    @Override
    public boolean preHandle(@Nonnull HttpServletRequest request,
                             @Nonnull HttpServletResponse response,
                             @Nonnull Object handler) throws Exception {
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        int status = response.getStatus();
        if (status < 200 || status >= 300) {
            return true;
        }
        //set empty user
        UACUserContext.setUser(UACUserInfo.anonymous());
        Result result = null;
        boolean enableAuth = !BeanUtil.isEmpty(this.properties) && Optional.ofNullable(this.properties.getEnable()).orElse(false);
        String token = tryGetToken(request);
        UACUserInfo userInfo = UACUserInfo.anonymous();
        userInfo.setToken(token);
        if (StrUtil.isBlank(token) && enableAuth) {
            result = Result.failure(ErrCode.TOKEN_INVALID.getCode(), "用户未登录或登录已失效");
        } else if (StrUtil.isNotBlank(token)) {
            try {
                val jwtToken = jwtUtil.verify(token);
                if (ObjectUtils.isEmpty(jwtToken) || Strings.isEmpty(jwtToken.getUserId())) {
                    throw Result.failure(ErrCode.TOKEN_INVALID.getCode(), "用户未登录或登录已失效").wrap();
                }
                val tid = jwtToken.getId();
                userInfo.setUserId(jwtToken.getUserId());
                userInfo.setName("");
                UACUserInfo cacheUserInfo = UserInfoCacheUtils.getUACUserInfoCacheOrFromDb(userAccountMapper,userInfosMapper, jwtToken.getUserId());
                if (cacheUserInfo == null) {
                    throw Result.failure(ErrCode.TOKEN_INVALID.getCode(), "用户不存在或已被禁用").wrap();
                }
                BeanUtil.copyProperties(cacheUserInfo, userInfo);
//                UACTokenInfo tokenInfo = TokenCacheUtils.getAuthCache(tid);
//                if (tokenInfo == null) {
//                    throw Result.failure(ErrCode.TOKEN_INVALID.getCode(), "用户未登录或登录已失效").wrap();
//                }
            } catch (Exception ex) {
                log.warn("check user token error : ", ex);
                result = wrapException(enableAuth, ex);
            }
        }
        if (result != null) {
            response.setContentType("application/json; charset=utf-8");
            PrintWriter out = response.getWriter();
            String jsonStr = JSONUtil.toJsonStr(result);
            out.write(jsonStr);
            out.flush();
            out.close();
            return false;
        }
        UACUserContext.setUser(userInfo);
        request.setAttribute(USER_KEY, userInfo);
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(@Nonnull HttpServletRequest request,
                                @Nonnull HttpServletResponse response,
                                @Nonnull Object handler,
                                Exception ex) throws Exception {
        UACUserContext.remove();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }


    private String tryGetToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (token == null) {
            token = request.getParameter("token");
        }
        return token;
    }
}
